跳到主要内容

网络问题排查思路

简单排查设备问题

确认内核模块 ip_tables 是否加载

lsmod | grep ip_tables

确认 iptable forward 是否默认 accpet

cat /proc/sys/net/ipv4/ip_forward

确认宿主机网络是否正常

ping 10.200.0.1

查看集群事件来获取可能的错误或警告:

kubectl get events --all-namespaces --sort-by='.metadata.creationTimestamp'


sudo journalctl -u k3s

网络连接不通排查流程

# 首先启动一个临时的 pod
$ kubectl run tmp-shell --rm -it --image nicolaka/netshoot -- bash

# kubectl exec <some-pod-in-same-namespace> -- curl -k https://cert-manager-webhook.cert-manager.svc:443

$ kubectl exec tmp-shell -- curl -k https://cert-manager-webhook.cert-manager.svc:443
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:02:11 --:--:-- 0
curl: (28) Failed to connect to cert-manager-webhook.cert-manager.svc port 443 after 131174 ms: Operation timed out
command terminated with exit code 28

发现网络连接超时了,那基本网络就有问题了

# 尝试 ping
ping cert-manager-webhook.cert-manager.svc
> PING cert-manager-webhook.cert-manager.svc.cluster.local (10.43.132.99) 56(84) bytes of data.

发现能解析出 IP,所以它就不是 DNS 的问题

$ kubectl get svc cert-manager-webhook -n cert-manager
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cert-manager-webhook ClusterIP 10.43.132.99 <none> 443/TCP 10d

$ kubectl get ep cert-manager-webhook -n cert-manager
NAME ENDPOINTS AGE
cert-manager-webhook 10.42.5.95:10250 10d

通过上面的命令可以找到目标端点的 IP 地址,发现这个地址是 Node 的 CNI 网络地址

$ ifconfig
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1370
inet 10.42.5.1 netmask 255.255.255.0 broadcast 10.42.5.255
inet6 fe80::6c2a:b4ff:fe98:787c prefixlen 64 scopeid 0x20<link>
ether 6e:2a:b4:98:78:7c txqueuelen 1000 (Ethernet)
RX packets 152184 bytes 23826794 (23.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 159198 bytes 76726244 (76.7 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

注意,这里 ping 不通的 IP,是因为网络地址是 cni0 网卡的地址,在这种情况下,这个 IP 地址很可能是某个 Pod 的 ClusterIP,它是 Kubernetes 服务的内部地址,用于在集群内部路由到正确的 Pod。这些地址通常不响应 ICMP 请求,如 ping,因为它们是虚拟的,由 kube-proxy 或 CNI 管理。

检查节点上的网络路由是否正确配置,以确保流量能够正确路由到 cni0 接口和 Pod 网络。

$ ip route show

10.42.0.0/24 dev cni0 proto kernel scope link src 10.42.0.1
10.42.1.0/24 via 10.42.1.0 dev flannel.1 onlink
10.42.2.0/24 via 10.42.2.0 dev flannel.1 onlink
10.42.5.0/24 via 10.42.5.0 dev flannel.1 onlink
....

这里的

10.42.0.0/24 dev cni0 proto kernel scope link src 10.42.0.1

这条路由指向 Kubernetes Pod 网络。cni0 是 CNI 网络插件(可能是 Flannel 或类似的插件)创建的一个桥接接口,用于该节点上的 Pod 通信。

10.42.1.0/24 via 10.42.1.0 dev flannel.1 onlink
10.42.2.0/24 via 10.42.2.0 dev flannel.1 onlink
10.42.5.0/24 via 10.42.5.0 dev flannel.1 onlink

这些路由表示跨节点的 Pod 网络。flannel.1 设备通常与 Flannel CNI 插件有关,用于处理跨节点的 Pod-to-Pod 通信。

可以看到上面的路由是没有问题的,下面继续分析流量是哪里有问题

使用工具如 tcpdump 来抓取和分析网络包,检查流量是否实际上按预期路由。

sudo apt-get update
sudo apt-get install tcpdump

使用 ip a 或 ifconfig 命令确定你的节点上有哪些网络接口。例如,你可能想在 eth0、flannel.1、cni0 或其他相关接口上捕获流量。例如这里就是为了确认 cert-manager-webhook 服务的 Pod 是否在接收到来自其他 Pod 的流量,所以应该在 cert-manager-webhook 所在的 Pod 的节点上抓包,并且监视与服务 IP 地址相关联的网络接口上的流量。

通常,服务的 ClusterIP 不直接绑定到任何接口上,而是由 kube-proxy 处理,可能转发到 cni0 或其他接口。以下是如何进行操作的步骤:

# 找出服务使用的端口
kubectl get svc cert-manager-webhook -n cert-manager

查找对应的端口 443,这里捕获所有进出 cni0 接口的与 443 端口相关的 TCP 流量,使用如下命令:

sudo tcpdump -i cni0 port 443 -nn

-nn 选项确保不将主机名和端口解析为名称

查看数据包是否包含 SYN 和 ACK 标志,这表示 TCP 握手正在进行。如果只看到 SYN 包,可能表示连接尝试没有成功。如果没有看到任何包,那么可能流量根本没有到达该接口,或者网络策略可能阻止了流量。

listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
15:54:53.138199 IP 10.42.1.6.48522 > 10.43.132.99.443: Flags [S], seq 2616082236, win 65170, options [mss 1330,sackOK,TS val 37533555 ecr 0,nop,wscale 7], length 0
15:54:54.165428 IP 10.42.1.6.48522 > 10.43.132.99.443: Flags [S], seq 2616082236, win 65170, options [mss 1330,sackOK,TS val 37534583 ecr 0,nop,wscale 7], length 0
15:54:56.181427 IP 10.42.1.6.48522 > 10.43.132.99.443: Flags [S], seq 2616082236, win 65170, options [mss 1330,sackOK,TS val 37536599 ecr 0,nop,wscale 7], length 0
15:55:00.341423 IP 10.42.1.6.48522 > 10.43.132.99.443: Flags [S], seq 2616082236, win 65170, options [mss 1330,sackOK,TS val 37540759 ecr 0,nop,wscale 7], length 0
15:55:08.533432 IP 10.42.1.6.48522 > 10.43.132.99.443: Flags [S], seq 2616082236, win 65170, options [mss 1330,sackOK,TS val 37548951 ecr 0,nop,wscale 7], length 0

这些数据包显示了几个重要的信息点:

  1. 时间戳:每个包前的时间戳显示了每个 SYN 数据包的发送时间。
  2. 源 IP10.42.1.6 是发送数据包的 Pod 的 IP 地址。
  3. 目的 IP10.43.132.99cert-manager-webhook 服务的 ClusterIP。
  4. 源端口48522 是源 Pod 从其发起连接的临时选定端口。
  5. 目的端口443 是目标服务监听的端口,通常用于 HTTPS。
  6. Flags [S]:每个数据包都有一个 SYN 标志,表明这是一个 TCP 连接请求的初始握手部分。
  7. 序列号seq 2616082236 是 TCP 握手中的初始序列号。
  8. 窗口大小win 65170 指的是发送方的接收窗口大小,这是对方可以发送的数据量,在等待确认之前不会被接收方接收。
  9. TCP 选项
    • mss 1330 是最大分段大小,这是 TCP 通信中每个数据包的最大数据量。
    • sackOK 表示发送方支持选择性确认。
    • TS valecr 是 TCP 时间戳,用于 RTT(往返时间)的计算。
    • nop 是无操作选项,用于对齐。
    • wscale 7 是窗口缩放选项,用于允许更大的窗口大小。
  10. length 0:表示这是一个纯 SYN 数据包,没有携带任何数据负载。

从这些数据包可以看出,发送方一直在尝试与 10.43.132.99 上的端口 443 建立 TCP 连接,但似乎并没有收到 SYN-ACK 响应。这是一个经典的三次握手中的第一步,但没有继续进行下去。由于没有 SYN-ACK 响应,发送方在一定间隔后重试发送 SYN 包(这被称为 TCP 重传),这些间隔通常会随着时间指数增长。

可以通过下面的命令找到这个 Pod 所在的节点

$ kubectl get svc --all-namespaces
cert-manager cert-manager-webhook ClusterIP 10.43.132.99 <none> 443/TCP 10d

$ kubectl get endpoints cert-manager-webhook -n cert-manager
NAME ENDPOINTS AGE
cert-manager-webhook 10.42.5.95:10250 10d

可以轻松找到这个节点就是 Node2

这些迹象表明,尝试连接到 cert-manager-webhook 服务的 Pod 无法建立 TCP 连接。原因可能包括:

  • 目标 Pod 没有正确运行或没有监听端口 443
  • 网络策略或防火墙规则阻止了访问。
  • 服务配置错误,没有将请求正确路由到后端的 Pod。
  • kube-proxy 问题,没有正确地处理流量转发。

进一步的排查应该集中在:

  • 确保 cert-manager-webhook Pod 正在正常运行且准备就绪。
  • 检查 Kubernetes 服务定义,确保它配置正确,并且选择器正确地指向了 Pod。(因为能解析到 DNS,所以应该没有问题)
  • 检查是否有任何网络策略可能会阻止 Pod 之间的通信。
  • 查看 kube-proxy 的日志以确认它是否在正常工作,并正确处理服务 IP 地址的流量。

因为 Pod 正常运行,所以下面开始排查防火墙策略,检查 Kubernetes 节点运行的是如 iptables、firewalld 或 ufw 等防火墙服务

# 对于 iptables
sudo iptables -L -n -v
sudo iptables -t nat -L -n -v

# 对于 firewalld
sudo firewall-cmd --list-all

# 对于 ufw
sudo ufw status verbose

过滤并查找特定规则:

$ sudo iptables -L -n -v | grep '10.42.5.95'
...

3 180 ACCEPT all -- * * 0.0.0.0/0 10.42.5.95 /* rule to permit the traffic traffic to pods when source is the pod's local node */ ADDRTYPE match src-type LOCAL
0 0 KUBE-NWPLCY-DEFAULT all -- * * 10.42.5.95 0.0.0.0/0 /* run through default egress network policy chain */
0 0 KUBE-NWPLCY-DEFAULT all -- * * 0.0.0.0/0 10.42.5.95 /* run through default ingress network policy chain */
0 0 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 0.0.0.0/0 10.42.5.95 /* rule to jump traffic destined to POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U3RF7N */
0 0 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 0.0.0.0/0 10.42.5.95 PHYSDEV match --physdev-is-bridged /* rule to jump traffic destined to POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U3RF7N */
0 0 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 10.42.5.95 0.0.0.0/0 /* rule to jump traffic from POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U3RF7N */
0 0 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 10.42.5.95 0.0.0.0/0 PHYSDEV match --physdev-is-bridged /* rule to jump traffic from POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U3RF7N */
15 1086 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 10.42.5.95 0.0.0.0/0 /* rule to jump traffic from POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U3RF7N */
15 1129 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 0.0.0.0/0 10.42.5.95 /* rule to jump traffic destined to POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U3RF7N */
0 0 KUBE-POD-FW-OQ7VMORGW3U3RF7N all -- * * 10.42.5.95 0.0.0.0/0 /* rule to jump traffic from POD name:cert-manager-webhook-869b6c65c4-pk4p4 namespace: cert-manager to chain KUBE-POD-FW-OQ7VMORGW3U

下面是对输出的逐行分析:

  1. 规则1 (3封包, 180字节)

    • ACCEPT all -- * * 0.0.0.0/0 10.42.5.95
    • 这个规则允许所有流量发往 10.42.5.95。它应用于源地址类型为 LOCAL 的流量,这意味着目标是本地节点上的Pod。
  2. 规则2和3 (0封包, 0字节)

    • KUBE-NWPLCY-DEFAULT all -- * * 10.42.5.95 0.0.0.0/0
    • KUBE-NWPLCY-DEFAULT all -- * * 0.0.0.0/0 10.42.5.95
    • 这些规则指示 iptables 将流量跳转至默认的网络策略链,用于执行 egress(出站)和 ingress(入站)网络策略。由于计数器都是0,这表示尚未有匹配这些规则的流量。
  3. 规则4到8 (其中某些规则 15封包, 1086和1129字节)

    • KUBE-POD-FW-<HASH> all -- * * 0.0.0.0/0 10.42.5.95
    • KUBE-POD-FW-<HASH> all -- * * 10.42.5.95 0.0.0.0/0
    • 这些规则涉及将流量跳转至一个特定的Pod防火墙链(KUBE-POD-FW-<HASH>)。HASH值是为特定的Pod生成的,这里的Pod为cert-manager-webhook-869b6c65c4-pk4p4,位于cert-manager命名空间中。
    • 部分规则涉及物理设备桥接(PHYSDEV match --physdev-is-bridged),这在Kubernetes中用于管理跨节点的Pod-to-Pod流量。
    • 最后两个规则(15封包计数)表示实际有流量到达或来自Pod10.42.5.95

每个规则的计数器(第一列和第二列的数字)告诉我们通过该规则的数据包数和字节数。计数器为零的规则表示没有流量匹配这些规则,而有计数的规则表示相应的流量被允许或者被处理了。

Ingress 无法访问

1、检查浏览器插件是否拦截了域名(代理插件)

2、检查 Ingress 资源

kubectl get ingress -n common-namespace
kubectl describe ingress <ingress-name> -n common-namespace

3、检查服务和端点

# 确保服务存在
kubectl get svc -n common-namespace
# 检查端点是否存在:
kubectl get endpoints <service-name> -n common-namespace

4、检查 DNS 解析:

  • 确保域名 jaeger.quicktoolset.top 正确解析到了 Ingress 控制器的公网 IP 地址。
  • 你可以使用 dignslookup 命令来检查 DNS 解析。

5、检查网络策略

  • 如果你的 Kubernetes 集群使用了网络策略(Network Policies),确保它们没有阻止进入到你的服务。

6、检查主机和路径规则

  • 确保 Ingress 中的主机和路径规则与你的服务匹配。
  • 检查是否有任何路径或主机的拼写错误。

7、检查 TLS/SSL 证书和密钥

  • 如果你配置了 TLS,确保引用的密钥和证书是有效的。
  • 查看 jaeger-cert-secret 的状态:kubectl describe secret jaeger-cert-secret -n common-namespace

8、检查防火墙和安全组规则

  • 确保你的云提供商或者本地环境的防火墙规则和安全组允许流量到达 Kubernetes 节点的 80 和 443 端口。

9、检查端口绑定

  • 确保你的 Ingress 控制器绑定了正确的端口。

10、访问日志

  • 如果可能,检查 Ingress 控制器的访问日志以确定流量是否到达控制器。

11、使用 curl 进行测试:

  • 从集群内部和外部使用 curl 命令测试你的 Ingress,以查看是否可以到达服务。
  • 比如:curl -H "Host: jaeger.quicktoolset.top" http://<Ingress-Controller-IP>/